using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Web.Mvc;
using System.Web.Routing;
using MvcContrib.Pagination;
using MvcContrib.Sorting;
using MvcContrib.UI.Grid;
using TaskManager.Domain.BusinessObjects;
using TaskManager.Domain.Exceptions;
using TaskManager.Domain.Repositories;
using TaskManager.Domain.Services;
using TaskManager.Web.Controls;
using TaskManager.Web.Filters;
using TaskManager.Web.Mappers;
using TaskManager.Web.ViewModels.Project;

namespace TaskManager.Web.Controllers
{
    [Log]
    [UpdateActiveTab]
    public class ProjectController : Controller
    {
        private static readonly log4net.ILog log = log4net.LogManager.GetLogger(typeof(ProjectController));

        private readonly IProjectRepository _projectRepository;
        private readonly IUnitOfWork _unitOfWork;
        private readonly IUserActionLogger _userActionLogger;
        private readonly Lazy<IProjectDeleteService> _projectDeleteService;
        private TabContainer _tabContainer;

        public ProjectController(IProjectRepository projectRepository, Lazy<IProjectDeleteService> projectDeleteService, IUnitOfWork unitOfWork, 
            IUserActionLogger userActionLogger, TabContainer tabContainer)
        {
            Contract.Requires(projectRepository != null);
            Contract.Requires(unitOfWork != null);

            _projectRepository = projectRepository;
            _tabContainer = tabContainer;
            _projectDeleteService = projectDeleteService;
            _unitOfWork = unitOfWork;
            _userActionLogger = userActionLogger;

            if(log.IsDebugEnabled) log.Debug("Created ProjectController");
        }

        //
        // GET: /Project/

        public ActionResult Index(int ? page, GridSortOptions sort, bool showClosedProjects=false)
        {
            IEnumerable<Project> projects;

            if (showClosedProjects)
            {
                projects = _projectRepository.AllIncluding(project => project.Tasks);
            }
            else
            {
                projects = _projectRepository.AllIncluding(project => project.Tasks)
                    .Where(project => project.StateImpl == (int)ProjectState.InProgress);
            }

            if(sort.Column == null)
                sort = new GridSortOptions { Column = "Name", Direction = SortDirection.Ascending };

            projects = projects
                .OrderBy(sort.Column, sort.Direction)
                .AsPagination(page ?? 1, 10);

            ViewBag.sort = sort;
            ViewBag.showClosedProjects = showClosedProjects;

            if (Request.IsAjaxRequest())
            {
                return PartialView("_ProjectList", projects);
            }

            return View(projects);
        }

        //
        // GET: /Project/Log/5

        public PartialViewResult Log(int id, GridSortOptions sort, int? page)
        {
            if (sort.Column == null)
                sort = new GridSortOptions { Column = "Created", Direction = SortDirection.Ascending };

            var logs = _projectRepository.Find(id)
                .Logs
                .OrderBy(sort.Column, sort.Direction)
                .AsPagination(page ?? 1, 10);

            ViewBag.sort = sort;

            return PartialView("_ProjectLog", logs);
        }

        //
        // GET: /Project/Details/5

        public ViewResult Details(int id, int? tab)
        {
            Project project = _projectRepository.Find(id);

            if (tab == null || tab == 0)
            {
                var routeValues = new RouteValueDictionary(HttpContext.Request.RequestContext.RouteData.Values);

                string tabName = project.Name;

                tab = _tabContainer.AddTab(tabName, "Details", "Project", Url, routeValues).TabId;
            }

            ViewBag.Tab = (int) tab;

            return View(project);
        }

        //
        // GET: /Project/Create

        public ActionResult Create()
        {
            return View();
        }

        //
        // POST: /Project/Close/5

        [HttpPost]
        public ActionResult Close(int id)
        {
            Project project = _projectRepository.Find(id);

            string projectName = project.Name;

            try
            {
                project.Close();
            }
            catch(UserActionFailureException error)
            {
                ModelState.AddModelError(string.Empty, error.Message);
                return View("Details", project);
            }

            _unitOfWork.Save();

            TempData["info"] = string.Format(Resource.ProjectController_Close, projectName); 

            return RedirectToAction("Index");
        }

        //
        // POST: /Project/Reopen/5

        [HttpPost]
        public ActionResult Reopen(int id)
        {
            Project project = _projectRepository.Find(id);

            string projectName = project.Name;

            try
            {
                project.Reopen();
            }
            catch (UserActionFailureException error)
            {
                ModelState.AddModelError(string.Empty, error.Message);
                return View("Details", project);
            }

            _unitOfWork.Save();

            TempData["info"] = string.Format(Resource.ProjectController_Reopen, projectName); 

            return RedirectToAction("Index");
        }


        //
        // POST: /Project/Create

        [HttpPost]
        public ActionResult Create([Bind(Exclude = "Id")]ProjectCreateOrEditModel model)
        {
            if (ModelState.IsValid)
            {
                if (_projectRepository.FindByName(model.Name) == null)  // Make sure project name is not in use
                {
                    var project = new Project(model.Name, model.Description, _userActionLogger);

                    _projectRepository.Insert(project);
                    _unitOfWork.Save();

                    return RedirectToAction("Index");
                }
                else
                {
                    ModelState.AddModelError("Name", Resource.ProjectController_Create_project_with_this_name_is_already_exist);
                }
            }

            return View();
        }

        //
        // GET: /Project/Edit/5

        public ActionResult Edit(int id)
        {
            Project project = _projectRepository.Find(id);

            var model = new ProjectCreateOrEditModel();

            ProjectMapper.Map(project, model);

            return View(model);
        }

        //
        // POST: /Project/Edit/5

        [HttpPost]
        public ActionResult Edit(ProjectCreateOrEditModel model)
        {
            if (ModelState.IsValid)
            {
                Project project = _projectRepository.Find(model.Id);

                if (!model.Name.Equals(project.Name, StringComparison.CurrentCultureIgnoreCase))    // Was project name changed?
                {
                    if(_projectRepository.FindByName(model.Name) != null)   // Name is already in use?
                    {
                        ModelState.AddModelError("Name", Resource.ProjectController_Create_project_with_this_name_is_already_exist);
                        
                        return View();
                    }
                }

                ProjectMapper.Map(model, project);

                _projectRepository.Update(project);
                _unitOfWork.Save();

                return RedirectToAction("Details", new { id = model.Id, tab = _tabContainer.ActiveTab });
            }

            return View();
        }

        //
        // POST: /Project/Delete/5

        [HttpPost, ActionName("Delete")]
        public ActionResult DeleteConfirmed(int id)
        {
            Project project = _projectRepository.Find(id);
            string projectName = project.Name;

            try
            {
                _projectDeleteService.Value.Delete(project);
                _unitOfWork.Save();
            }
            catch (UserActionFailureException error)
            {
                ModelState.AddModelError(string.Empty, error.Message);
                return View("Details", project);
            }

            TempData["info"] = string.Format(Resource.ProjectController_DeleteConfirmed, projectName);
            
            return RedirectToAction("Index");
        }
    }
}

